package cn.yangliu.swagger2;

import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import javax.annotation.Resource;

import cn.yangliu.swagger2.web.Swagger2Controller;
import com.github.xiaoymin.swaggerbootstrapui.annotations.EnableSwaggerBootstrapUI;
import io.swagger.annotations.ApiOperation;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.util.StringUtils;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.ApiSelectorBuilder;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger.web.DocExpansion;
import springfox.documentation.swagger.web.ModelRendering;
import springfox.documentation.swagger.web.SwaggerResource;
import springfox.documentation.swagger.web.SwaggerResourcesProvider;
import springfox.documentation.swagger.web.UiConfiguration;
import springfox.documentation.swagger.web.UiConfigurationBuilder;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

/**
 * @author 杨柳
 * 创建时间 2017-09-04 18:50
 */
@Configuration
@EnableConfigurationProperties(value = Swagger2Properties.class)
@ConditionalOnProperty(prefix = "spring.swagger2", value = "enable", matchIfMissing = true)
@EnableSwagger2
@Primary
@EnableSwaggerBootstrapUI
public class Swagger2AutoConfiguration {


    @Bean
    public Swagger2Controller swagger2Controller() {
        return new Swagger2Controller();
    }

    @Resource
    private Swagger2Properties properties;

    @Bean
    @ConditionalOnMissingBean(Docket.class)
    public Docket createRestApi(ApiInfo apiInfo) {
        Docket docket = new Docket(DocumentationType.SWAGGER_2);
        docket.apiInfo(apiInfo);
        docket.host(properties.getHost());
        ApiSelectorBuilder builder = docket.select();
        if (!StringUtils.isEmpty(properties.getBasePackge())) {
            builder.apis(RequestHandlerSelectors.basePackage(properties.getBasePackge()));
        } else {
            builder.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class));
        }

        docket = builder.paths(PathSelectors.any()).build();
        if (Objects.nonNull(properties.getSecuritySchemes())) {
            List<Swagger2Properties.SecuritySchemes.ApiKey> apiKeys = properties.getSecuritySchemes().getApiKeys();
            if (apiKeys != null && !apiKeys.isEmpty()) {
                docket = docket.securitySchemes(apiKeys.stream().map(Swagger2Properties.SecuritySchemes.ApiKey::build).collect(Collectors.toList()));
            }
            List<Swagger2Properties.SecuritySchemes.BasicAuth> basicAuths =
                    properties.getSecuritySchemes().getBasicAuths();
            if (basicAuths != null && !basicAuths.isEmpty()) {
                docket = docket.securitySchemes(basicAuths.stream().map(Swagger2Properties.SecuritySchemes.BasicAuth::build).collect(Collectors.toList()));
            }

        }

        /*
         * 设置全局参数
         */
        Set<String> produces = properties.getProduces();
        if (Objects.nonNull(produces) && !produces.isEmpty()) {
            docket.produces(produces);
        }

        List<Swagger2Properties.Parameter> parameters = properties.getGlobalOperationParameters();
        if (Objects.nonNull(parameters) && !parameters.isEmpty()) {
            docket.globalOperationParameters(parameters.stream().map(Swagger2Properties.Parameter::build).collect(Collectors.toList()));
        }
        //基础url
        if (!StringUtils.isEmpty(properties.getPathMapping())) {
            docket.pathMapping(properties.getPathMapping());
        }

        return docket;
    }


    @Bean
    @ConditionalOnMissingBean(ApiInfo.class)
    public ApiInfo apiInfo() {
        ApiInfoBuilder builder = new ApiInfoBuilder();
        builder.title(properties.getTitle())
                .contact(properties.getContact().build())
                .description(properties.getDescription())
                .termsOfServiceUrl(properties.getTermsOfServiceUrl())
                .version(properties.getVersion());
        return builder.build();
    }

    @Bean
    @Primary
    @ConditionalOnExpression("${spring.swagger2.rewirteProvider:false}")
    public DocumentationConfig documentationConfig() {
        return new DocumentationConfig(properties);
    }


    static class DocumentationConfig implements SwaggerResourcesProvider {
        private Swagger2Properties properties;

        DocumentationConfig(Swagger2Properties properties) {
            this.properties = properties;
        }

        @Override
        public List<SwaggerResource> get() {
            return properties.getResources();
        }
    }

    @Bean
    @ConditionalOnExpression("${spring.swagger2.rewirteUiConfig:false}")
    public UiConfiguration uiConfiguration() {
        return UiConfigurationBuilder.builder()
                .validatorUrl(null)
                .docExpansion(DocExpansion.of("list"))
                .defaultModelRendering(ModelRendering.of("schema"))
                .supportedSubmitMethods(UiConfiguration.Constants.DEFAULT_SUBMIT_METHODS).build();
    }

}
